package com.my.storage.service.impl;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.Timestamp;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import com.my.storage.store.PathInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.NamedThreadLocal;
import org.springframework.core.io.FileSystemResource;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;

import com.my.storage.dto.FileDownDTO;
import com.my.storage.exception.error.BusinessException;
import com.my.storage.mapper.FileMapper;
import com.my.storage.model.FileMetadata;
import com.my.storage.service.FileService;
import com.my.storage.store.FileUtils;
import com.my.storage.store.PathStore;

/**
 * @author xhw
 */
@Service
public class FileServiceImpl implements FileService {

    private static final Logger logger = LoggerFactory.getLogger(FileServiceImpl.class);

    private final FileMapper fileMapper;
    //保存文件的元数据到数据库，保存文件内容到本地系统
    //上传
    //通过构造函数注入，不要在字段注入
    //不能被单元测试

    private String uploadDir;
    ExecutorService executorService;

    public FileServiceImpl(FileMapper fileMapper, @Value("${file.upload-dir}") String uploadDir) {
        this.fileMapper = fileMapper;
        this.uploadDir = uploadDir;
        executorService = Executors.newScheduledThreadPool(1, NamedThreadLocal);
    }


    //删除目录
    public void deleteFile(String logicPath) {
        Assert.hasText(logicPath, "路径不能为空");

        String normalizedPath = PathStore.normalizePath(logicPath);
        //查找数据库中是否有对应的文件或目录记录
        FileMetadata fileMetadata = fileMapper.findByPath(normalizedPath);
        if (fileMetadata == null) {
            throw new BusinessException("FILE_NOT_FOUND", "文件或目录不存在：" + logicPath);
        }

        //0,1,2;

        //删除数据库记录
        fileMapper.deleteByPath(normalizedPath);

        //获取文件或目录在文件系统中的存储路径
        File fileToDelete = new File(uploadDir + File.separator + normalizedPath);

        //递归删除文件和目录
        // 异步递归删除文件和目录
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.submit(() -> {
            try {
                executeDelete(fileToDelete);
                logger.info("文件或目录删除成功：" + logicPath);
            } catch (Exception e) {
                logger.error("异步删除失败：" + logicPath, e);
            }
        });
    }

    private void executeDelete(File fileToDelete) {
        if (fileToDelete.isDirectory()) {
            File[] files = fileToDelete.listFiles();
            if (files != null) {
                for (File file : files) {
                    executeDelete(file);
                }

            }
        }
    }


//    //添加异步删除文件和目录的方法
//    @Async
//    protected void deleteFiles(File fileToDelete) {
//        if(fileToDelete.isDirectory()) {
//            File[] files = fileToDelete.listFiles();
//            if(files != null) {
//                for(File file : files) {
//                    deleteFiles(file);
//                }
//            }
//        }
//        if (!fileToDelete.delete()) {
//            logger.error("删除文件或目录失败：" + fileToDelete.getAbsolutePath());
//            throw new BusinessException("DELETE_ERROR", "删除文件或目录失败：" + fileToDelete.getAbsolutePath());
//        }
//    }

    //查询单个文件
    public FileMetadata getFileDetails(String logicPath) {
        Assert.hasText(logicPath, "路径不能为空");

        String normalizedPath = PathStore.normalizePath(logicPath);
        FileMetadata fileMetadata = fileMapper.findByPath(normalizedPath);

        if (fileMetadata == null) {
            throw new BusinessException("FILE_NOT_FOUND", "文件未找到");
        }
        logger.info("获取文件元数据成功: {}", normalizedPath);
        return fileMetadata;
    }

    //构建下载文件的路径并且返回一个FileSystemResource
    public FileSystemResource getFileSystemResource(String logicPath) {
        String normalizedPath = PathStore.normalizePath(logicPath);

        // 查询元数据以检查文件是否存在并且不能是目录
        FileMetadata fileMetadata = fileMapper.findByPath(normalizedPath);
        if (fileMetadata == null || fileMetadata.getType().isEmpty()) {
            throw new BusinessException("FILE_NOT_FOUND", "文件不存在");
        }
        //构建文件路径
        Path filePath = Paths.get(uploadDir, normalizedPath);
        return new FileSystemResource(filePath.toFile());
    }

    //查询所有文件
    public List<FileMetadata> listChildren(String logicPath) {
        String normalizedPath = PathStore.normalizePath(logicPath);
        int currentDepth = PathStore.calculateDepth(normalizedPath);

        if (normalizedPath.equals("/Root") || normalizedPath.equals("/")) {
            return fileMapper.findRootFiles();
        } else {
            normalizedPath += "/%";
            return fileMapper.findFilesByPathAndDepth(normalizedPath, currentDepth + 1);
        }
    }

    public FileMetadata saveFile(MultipartFile file, String logicPath) {
        Assert.notNull(file, "文件不能为空");
        if (logicPath.endsWith("/")) {
            throw new BusinessException("PATH_ERROR", "不可以上传目录");
        }

        // 生成存储路径和文件名
        PathInfo pathInfo = generatePathInfo(logicPath);

        // 先保存物理文件
        savePhysicalFile(file, pathInfo.getStoragePath(), pathInfo.getFilename());

        // 保存目录的元数据（如果存在）
        saveDirectoryMetadata(pathInfo.getStoragePath());

        // 再保存文件元数据
        return saveMetadata(logicPath, pathInfo.getFilename(), file);
    }

    private PathInfo generatePathInfo(String logicPath) {
        String normalizedPath = PathStore.normalizePath(logicPath);
        Path path = Paths.get(uploadDir, normalizedPath);
        String storagePath = path.getParent().toString();
        String filename = path.getFileName().toString();
        return new PathInfo(storagePath, filename);
    }

    //保存物理文件
    private void savePhysicalFile(MultipartFile file, String storagePath, String filename) {
        File targetFile = new File(storagePath, filename);
        try {
            //创建目录结构并保存文件
            Files.createDirectories(Paths.get(storagePath));
            //确保数据完全覆盖
            if (targetFile.exists()) {
                targetFile.delete();
            }
            //保存物理文件
            FileUtils.saveFile(file.getInputStream(), targetFile);
        } catch (IOException e) {
            logger.error("创建目录或者保存文件失败：", e);
            throw new BusinessException("FILE_SAVE_ERROR", "创建目录或保存文件失败：" + e.getMessage());
        }
    }

    private void saveDirectoryMetadata(String storagePath) {
        String[] pathParts = storagePath.split("/");
        StringBuilder currentPath = new StringBuilder();
        for (int i = 1; i < pathParts.length - 1; i++) {
            currentPath.append("/").append(pathParts[i]);
            String dirPath = currentPath.toString();

            if (!fileMapper.existsPath(dirPath)) {
                FileMetadata dirMetadata = new FileMetadata();
                dirMetadata.setPath(dirPath);
                dirMetadata.setDepth(PathStore.calculateDepth(dirPath));
                dirMetadata.setType("");
                dirMetadata.setUploadTime(new Timestamp(System.currentTimeMillis()));
                upsertMetadata(dirMetadata);
            }
        }
    }

    private FileMetadata saveMetadata(String logicPath, String filename, MultipartFile file) {
        String type = "";
        int dotIndex = filename.lastIndexOf(".");
        if (dotIndex > 0) {
            type = filename.substring(dotIndex + 1);
        }

        FileMetadata fileMetadata = new FileMetadata();
        fileMetadata.setPath(logicPath);
        fileMetadata.setDepth(PathStore.calculateDepth(logicPath));
        fileMetadata.setType(type);
        fileMetadata.setSize((int) file.getSize());
        fileMetadata.setUploadTime(new Timestamp(System.currentTimeMillis()));

        upsertMetadata(fileMetadata);
        return fileMetadata;
    }

    //更新文件元数据（兜底）
    private void upsertMetadata(FileMetadata metadata) {
        try {
            fileMapper.insert(metadata);
        } catch (org.springframework.dao.DuplicateKeyException e) {
            fileMapper.update(metadata);
        }
    }

}
